import re

from rest_framework import serializers
from django_redis import get_redis_connection

from goods.models import SKU
from users import constants
from users.models import User, Address


class AddressViewSerializer(serializers.ModelSerializer):
    '''地址新增序列化器'''
    province_id = serializers.IntegerField(label='省ID')
    city_id = serializers.IntegerField(label='市ID')
    district_id = serializers.IntegerField(label='区县ID')
    province = serializers.StringRelatedField(label='省名称', read_only=True)
    city = serializers.StringRelatedField(label='市名称', read_only=True)
    district = serializers.StringRelatedField(label='区县名称', read_only=True)

    class Meta:
        model = Address
        exclude = ('user', 'is_deleted', 'create_time', 'update_time')

    # 校验手机号格式
    def validate_mobile(self, value):
        if not re.match(r'^1[3-9]\d{9}$', value):
            raise serializers.ValidationError('手机号格式错误')
        return value

    def create(self, validated_data):
        '''我们的user参数没有传递,所以需要重写create方法手动添加'''
        # 获取序列化器的context属性的request参数来获取user
        user = self.context['request'].user
        # 将user添加到数据中
        validated_data['user'] = user
        # 调用父类的create方法
        return super().create(validated_data)


class AddressTitleSerializer(serializers.ModelSerializer):
    '''修改标题序列化器类'''

    class Meta:
        model = Address
        fields = ('title',)


# 定义序列化器
class UserSerializer(serializers.ModelSerializer):
    # 添加模型类中没有的字段,这些字段只在反序列化是校验的时候使用
    sms_code = serializers.CharField(label='短信验证码', write_only=True)
    allow = serializers.CharField(label='同意协议', write_only=True)
    password2 = serializers.CharField(label='密码', write_only=True)
    # 增加token字段,只用于序列化
    token = serializers.CharField(label='状态记录token', read_only=True)

    class Meta:
        model = User
        fields = ('id', 'username', 'mobile', 'password', 'password2', 'allow', 'sms_code', 'token')
        # 添加一些额外的参数
        extra_kwargs = {
            'username': {
                'min_length': 5,
                'max_length': 20,
                'error_messages': {
                    'min_length': '用户名在5-20个字符',
                    'max_length': '用户名在5-20个字符'
                }

            },
            # 密码只用与反序列化校验,和保存,序列时永不到
            'password': {
                'write_only': True,
                'min_length': 8,
                'max_length': 20,
                'error_messages': {
                    'min_length': '密码在8-20个字符',
                    'max_length': '密码在8-20个字符'

                }

            }
        }

    # 实现检验功能
    # 验证手机号
    def validate_mobile(self, value):
        if not re.match(r'^1[3-9]\d{9}', value):
            raise serializers.ValidationError('手机号不匹配')

        # 判断手机号是否存在

        count = User.objects.filter(mobile=value).count()

        if count:
            raise serializers.ValidationError('手机号已经存在')

        return value

    # 检验是否同意协议
    def validate_allow(self, value):
        if value != 'true':
            raise serializers.ValidationError('请同意用户协议')
        return value

    # 校验用户名
    def validate_username(self, value):
        if value.isdigit():
            raise serializers.ValidationError('用户名不可以为纯数字')

        return value

    # 校验密码和短信验证码
    def validate(self, attrs):

        # 校验密码
        password = attrs['password']
        password2 = attrs['password2']
        if password != password2:
            raise serializers.ValidationError('两次密码不一致')

        # 校验sms_code
        mobile = attrs['mobile']
        sms_code = attrs['sms_code']
        # 创建redis连接对象
        redis_coon = get_redis_connection('verify_codes')
        # 取出redis中的sms_code
        real = redis_coon.get('sms_code_%s' % mobile)  # bytes 类型

        # 如果为空
        if not real:
            raise serializers.ValidationError('验证码不存在')
        # 　解码
        real = real.decode()
        # 如果验证码不一致
        if sms_code != real:
            raise serializers.ValidationError('验证码有误')

        return attrs

    def create(self, validated_data):

        # 删除出数据库中不需要的字段
        del validated_data['password2']
        del validated_data['allow']
        del validated_data['sms_code']

        # drf 的认证系统提供了create_user()的方法,会自动将用户的密码进行加密
        user = User.objects.create_user(**validated_data)

        # 创建对象时生成token
        from rest_framework_jwt.settings import api_settings
        # 生成载荷的方法
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        # 生成token的方法
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

        # 生成载荷
        payload = jwt_payload_handler(user)
        # 生成token
        token = jwt_encode_handler(payload)

        # 给user添加token字段,将token返回给客户端
        user.token = token

        return user


# 定义用户详情序列类
class UserDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'mobile', 'email', 'email_active')


# 定义邮箱设置序列化类
class EmailSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'email')
        extra_messages = {
            'email': {
                'required': True  # 邮箱必须传
            }
        }

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email')
        instance.save()
        # 获取验证连接
        verify_url = instance.generate_verify_email_url()

        from celery_tasks.email.tasks import send_verify_email
        # 异步发送邮件
        send_verify_email.delay('m15824898302_1@163.com', verify_url)

        return instance


# 用户浏览记录序列化器
class HistoryViewSerializer(serializers.Serializer):
    sku_id = serializers.IntegerField(label='商品id', min_value=1)

    def validate_sku_id(self, value):
        # 判断id是否存在
        try:
            SKU.objects.get(id=value)
        except SKU.DoesNotExist:
            raise serializers.ValidationError('商品ID不存在')

        return value

    def create(self, validated_data):
        # 获取登陆用户的id
        user_id = self.context['request'].user.id
        # 获取sku_id
        sku_id = validated_data['sku_id']

        from django_redis import get_redis_connection
        redis_conn = get_redis_connection('history')

        # 移除已有sku_id的记录
        redis_conn.lrem('history_%s' % user_id, 0, sku_id)
        # 新增记录
        redis_conn.lpush('history_%s' % user_id, sku_id)
        # 保存指定数量的记录
        redis_conn.ltrim('history_%s' % user_id, 0, constants.USER_BROWSING_HISTORY_COUNTS_LIMIT - 1)

        return validated_data
